
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#ifdef _MSC_VER
#else
#include <unistd.h>
#include <netinet/tcp.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#endif

#include "client_net_new.h"

#include "retcodes.h"
#include "log.h"

namespace common
{
namespace net
{
#ifdef _MSC_VER
	void CClientNet::TimerActive()
	{
		CheckConnect();
		TimerStart();
	}

	CClientNet::CClientNet()
	{
		send_buffer_size_ = 0;
		receive_buffer_size_ = 0;
		connected_ = false;
	}

	CClientNet::~CClientNet()
	{
	}

	int32_t CClientNet::Initialize(uint32_t receive_buffer_capacity, uint32_t send_buffer_capacity)
	{
		receive_buffer_size_ = receive_buffer_capacity;
		send_buffer_size_ = send_buffer_capacity;
		return CChannelBase::Initialize(receive_buffer_capacity, send_buffer_capacity);
	}

	int32_t CClientNet::Finalize()
	{
		return CChannelBase::Finalize();
	}

	int CClientNet::CheckConnect()
	{
		if (connected() == true)
		{
			return ::common::SUCCEED;
		}

		int32_t retcode = Connect(ip(), port());
		if (retcode == ::common::SUCCEED)
		{
			LOG_INFO("default", "reconnect [%s:%u] ::common::SUCCEED", ip(), port());
		}
		else
		{
			LOG_WARN("default", "reconnect [%s:%u] failed", ip(), port());
		}
		return retcode;
	}

	int32_t CClientNet::Connect(const char* address, uint16_t port)
	{
		set_ip(address);
		set_port(port);
		set_key(time(NULL));
		if (fd_ != -1)
		{
			return ECLIENT_ALREADY_CONNECTED;
		}
		std::string addr_string(address);
		client_socket_.Connect(addr_string, port);
		fd_ = client_socket_.GetListenFD();
		if (fd_ == -1)
		{
			return ECLIENT_CREATE_SOCKET_FAIL;
		}
		int32_t register_code = CNetEventManager::GetInstance().RegisterNetEvent(&client_socket_, 1, this);
		if (register_code != ::common::SUCCEED)
		{
			fd_ = -1;
			return register_code;
		}
		connected_ = true;
		OnConnect();
		return register_code;
	}

	int32_t CClientNet::Disconnect()
	{
		int32_t retcode = CNetEventManager::GetInstance().UnregisterNetEvent(client_socket_.GetListenFD());
		if (retcode != ::common::SUCCEED)
		{
			return retcode;
		}
		client_socket_.Close();
		fd_ = -1;
		input_streams_.Clear();
		output_streams_.Clear();
		OnDisconnect();
		connected_ = false;
		return ::common::SUCCEED;
	}

	int CClientNet::EnableReconnect(int32_t interval)
	{
		TimerInit(interval);
		return TimerStart();
	}

	int CClientNet::DisableReconnect()
	{
		return TimerStop();
	}

	void CClientNet::OnNetEvent(int fd, uint32_t events)
	{
		int received = ::recv(fd_, (char*)input_streams_.WriteBuffer(), input_streams_.FreeSize(), 0);
		if (received > 0)
		{
			input_streams_.AddWriteBufSize(received);
			OnMsgEvent();
		}
		else if (received == 0 || errno != EWOULDBLOCK)
		{
			LOG_ERROR("default", "fd %d close errno %d( %s )", fd_, errno, strerror(errno));
			Disconnect();
		}
	}
#else
void CClientNet::TimerActive()
{
    CheckConnect();
    TimerStart();
}

CClientNet::CClientNet()
{
    send_buffer_size_ = 0;
    receive_buffer_size_ = 0;
    connected_ = false;
}

CClientNet::~CClientNet()
{
}

int32_t CClientNet::Initialize(uint32_t receive_buffer_capacity, uint32_t send_buffer_capacity)
{
    receive_buffer_size_ = receive_buffer_capacity;
    send_buffer_size_ = send_buffer_capacity;
    return CChannelBase::Initialize(receive_buffer_capacity, send_buffer_capacity);
}

int32_t CClientNet::Finalize()
{
    return CChannelBase::Finalize();
}

int CClientNet::CheckConnect()
{
    if (connected() == true)
    {
        return ::common::SUCCEED;
    }

    int32_t retcode = Connect(ip(), port());
    if (retcode == ::common::SUCCEED)
    {
        LOG_INFO("default", "reconnect [%s:%u] ::common::SUCCEED", ip(), port());
    }
    else
    {
        LOG_WARN("default", "reconnect [%s:%u] failed", ip(), port());
    }
    return retcode;
}

int32_t CClientNet::Connect(const char* address, uint16_t port)
{
    set_ip(address);
    set_port(port);
    set_key(time(NULL));
    if (fd_ != -1)
    {
        return ECLIENT_ALREADY_CONNECTED;
    }
    fd_ = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (fd_ == -1)
    {
        return ECLIENT_CREATE_SOCKET_FAIL;
    }
    sockaddr_in addr;
    memset(&addr, 0, sizeof(sockaddr_in));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr(address);
    addr.sin_port = htons(port);
    if (::connect(fd_, (sockaddr*)(&addr), sizeof(addr)) != 0)
    {
        close(fd_);
        fd_ = -1;
        return ECLIENT_CONNECT_FAIL;
    }
    int nodelay = 1;
    if (setsockopt(fd_, IPPROTO_TCP, TCP_NODELAY, &nodelay, sizeof(nodelay)) != 0)
    {
        close(fd_);
        fd_ = -1;
        return ECLIENT_NODELAY_FAIL;
    }
    int flags = fcntl(fd_, F_GETFL, 0);
    if (fcntl(fd_, F_SETFL, flags |= O_NONBLOCK) != 0)
    {
        close(fd_);
        fd_ = -1;
        return ECLIENT_NONBLOCK_FAIL;
    }
    if (setsockopt(fd_, SOL_SOCKET, SO_SNDBUF, (char*)(&send_buffer_size_), sizeof(send_buffer_size_)) != 0)
    {
        close(fd_);
        fd_ = -1;
        return ECLIENT_SET_SNDBUF_FAIL;
    }
    if (setsockopt(fd_, SOL_SOCKET, SO_RCVBUF, (char*)(&receive_buffer_size_), sizeof(receive_buffer_size_)) != 0)
    {
        close(fd_);
        fd_ = -1;
        return ECLIENT_SET_RCVBUF_FAIL;
    }
    int32_t register_code = CNetEventManager::GetInstance().RegisterNetEvent(fd_, EPOLLIN | EPOLLHUP | EPOLLERR, this);
    if (register_code != ::common::SUCCEED)
    {
        close(fd_);
        fd_ = -1;
        return register_code;
    }
	connected_ = true;
    OnConnect();
    return register_code;
}

int32_t CClientNet::Disconnect()
{
    int32_t retcode = CNetEventManager::GetInstance().UnregisterNetEvent(fd_);
    if (retcode != ::common::SUCCEED)
    {
        return retcode;
    }
    close(fd_);
    fd_ = -1;
    input_streams_.Clear();
    output_streams_.Clear();
    OnDisconnect();
    connected_ = false;
    return ::common::SUCCEED;
}

int CClientNet::EnableReconnect(int32_t interval)
{
    TimerInit(interval);
    return TimerStart();
}

int CClientNet::DisableReconnect()
{
    return TimerStop();
}

void CClientNet::OnNetEvent(int fd, uint32_t events)
{
    int received = ::recv(fd_, input_streams_.WriteBuffer(), input_streams_.FreeSize(), 0);
    if (received > 0)
    {
        input_streams_.AddWriteBufSize(received);
        OnMsgEvent();
    }
    else if (received == 0 || errno != EWOULDBLOCK)
    {
        LOG_ERROR("default", "fd %d close errno %d( %s )", fd_, errno, strerror(errno));
        Disconnect();
    }
}
#endif
}
}
